const osn = require('obs-studio-node')


const {OBSHandler} = require('./osn-hook')
const log = require('./log.js');
const remote = require('@electron/remote');
const { v4: uuidv4 } = require('uuid');
const {ipcRenderer} = require('electron')
const {UtilToolService} = require('./utils')



const osnJssdk = osn

function getPrimaryDisplayAsync() {
    return ipcRenderer.sendSync('api:screen:getPrimaryDisplayAsync');
}


const logger = log.fileLog;

var scene;
let existingWindow = false;
let initY = 0;
var displayId = 'display1';



function busySleep(sleepDuration) {
    var now = new Date().getTime();
    while (new Date().getTime() < now + sleepDuration) { /* do nothing */
    }

}


function createScene(type) {

    const sceneName = 'create_test_scene' + UtilToolService.createUUID();
    // Creating scene
    scene = osnJssdk.SceneFactory.create(sceneName);

    const {physicalWidth, physicalHeight, aspectRatio} = displayInfo();
    // Set output video size to 1920x1080
    const outputWidth = 1920;
    const outputHeight = Math.round(outputWidth / aspectRatio);

    // if(type==='screen'){
    //   const videoSource = createDesktopSource();
    //   logger.debug({videoSource})
    //
    //
    //   // setSetting('Video', 'Base', `${outputWidth}x${outputHeight}`);
    //   // setSetting('Video', 'Output', `${outputWidth}x${outputHeight}`);
    //   const videoScaleFactor = physicalWidth / outputWidth;
    //
    //   // Update source settings:
    //   let settings = videoSource.settings;
    //   logger.debug({
    //     settings
    //   })
    //   settings['width'] = physicalWidth;
    //   settings['height'] = physicalHeight;
    //   videoSource.update(settings);
    //   videoSource.save();
    //
    //   const sceneItem = scene.add(videoSource);
    //   sceneItem.scale = { x: 1.0/ videoScaleFactor, y: 1.0 / videoScaleFactor };
    //
    //   // sceneItem.selected = true
    //   // sceneItem.rotation = -10;
    //
    //   // console.log(sceneItem)
    // }else if(type=='camera'){
    //   const cameraSource = getCameraSource();
    //   if (cameraSource) {
    //     const cameraItem = scene.add(cameraSource);
    //     logger.debug({
    //       cameraItem
    //     })
    //     // cameraItem.rotation = 10;
    //     cameraItem.selected = true
    //
    //     // const cameraScaleFactor = 1.0 / (3.0 * cameraSource.width / outputWidth);
    //     // cameraItem.scale = { x: cameraScaleFactor, y: cameraScaleFactor };
    //     // cameraItem.position = {
    //     //   x: outputWidth - cameraSource.width * cameraScaleFactor - outputWidth / 10,
    //     //   y: outputHeight - cameraSource.height * cameraScaleFactor - outputHeight / 10,
    //     // };
    //   }
    // }


    const videoSource = createDesktopSource();
    // logger.debug({videoSource});


    // setSetting('Video', 'Base', `${outputWidth}x${outputHeight}`);
    // setSetting('Video', 'Output', `${outputWidth}x${outputHeight}`);
    const videoScaleFactor = physicalWidth / outputWidth;

    // Update source settings:
    // let settings = videoSource.settings;
    // logger.debug({
    //   settings,
    // });
    // settings['width'] = physicalWidth;
    // settings['height'] = physicalHeight;
    // videoSource.update(settings);
    // videoSource.save();

    // const sceneItem = scene.add(videoSource);
    // sceneItem.scale = {x: 1.0 / videoScaleFactor, y: 1.0 / videoScaleFactor};


    const videoSource2 = osnJssdk.InputFactory.create('window_capture', 'window-source');
    let settings2 = videoSource2.settings;
    let windows = videoSource2.properties.get('window').details.items;
    console.log({windows});
    settings2['window'] = windows[1].value; //first detected 'window' in the array of windows

    console.log({settings2});
    videoSource2.update(settings2);
    videoSource2.save();

    console.log({videoSource2});
    // const windowInputSource = osnJssdk.InputFactory.create('window_capture', 'window', {});
    //
    // let windowInputSourceSettings = windowInputSource.settings;
    // let windows = windowInputSource.properties.get('window').details.items;
    // console.log({windows})
    // windowInputSourceSettings['window'] = windows[0].value; //first detected 'window' in the array of windows
    // windowInputSource.update(windowInputSourceSettings);
    // windowInputSource.save();

    // console.log(windowInputSource)
    const sceneItemForWindow = scene.add(videoSource2);

    // sceneItem.selected = true
    // sceneItem.rotation = -10;

    // console.log(sceneItem)

    // const cameraSource = getCameraSource();
    // if (cameraSource) {
    //   const cameraItem = scene.add(cameraSource);
    //   logger.debug({
    //     cameraItem,
    //   });
    //   // cameraItem.rotation = 10;
    //   cameraItem.selected = true;
    //
    //   // const cameraScaleFactor = 1.0 / (3.0 * cameraSource.width / outputWidth);
    //   // cameraItem.scale = { x: cameraScaleFactor, y: cameraScaleFactor };
    //   // cameraItem.position = {
    //   //   x: outputWidth - cameraSource.width * cameraScaleFactor - outputWidth / 10,
    //   //   y: outputHeight - cameraSource.height * cameraScaleFactor - outputHeight / 10,
    //   // };
    // }


    return {scene, sceneName};
}

function createSource() {
    return null;
}

function createDesktopSource() {
    const type = 'monitor_capture';
    logger.debug({
        type,
    });
    var settings = {
        // capture_cursor: true,
        // monitor: 0,
    };
    // settings['capture_cursor'] = false;
    // settings['method'] = 0;
    // settings['monitor_wgc'] = 0;
    // settings['compatibility'] = false;
    const input = osnJssdk.InputFactory.create('monitor_capture', 'input', settings);

    return input;

}

function getCameraSource() {
    logger.warn('Trying to set up web camera...');

    // Setup input without initializing any device just to get list of available ones
    const dummyInput = osnJssdk.InputFactory.create('dshow_input', 'video', {
        audio_device_id: 'does_not_exist',
        video_device_id: 'does_not_exist',
    });

    const cameraItems = dummyInput.properties.get('video_device_id').details.items;

    logger.debug(`cameraItems is`, cameraItems);
    dummyInput.release();

    if (cameraItems.length === 0) {
        logger.debug('No camera found!!');
        return null;
    }

    const deviceId = cameraItems[0].value;
    cameraItems[0].selected = true;
    logger.debug('cameraItems[0].name: ' + cameraItems[0].name);


    const settings = {
        active: true,
        audio_output_mode: 0,
        color_range: 'default',
        color_space: 'default',
        frame_interval: -1,
        res_type: 0,
        video_format: 0,
    };
    settings['video_format'] = 1;
    settings['autorotation'] = true;
    settings['hw_decode'] = false;

    // settings['audio_output_mode'] = 1;
    // settings['res_type'] = 1;
    // settings['video_format'] = 2;
    const obsCameraInput = osnJssdk.InputFactory.create('dshow_input', 'video', {
        video_device_id: deviceId,
        ...settings,

    });

    // logger.debug(obsCameraInput);

    // It's a hack to wait a bit until device become initialized (maximum for 1 second)
    // If you know proper way how to determine whether camera is working and how to subscribe for any events from it, create a pull request
    // See discussion at https://github.com/Envek/obs-studio-node-example/issues/10
    for (let i = 1; i <= 4; i++) {

        logger.debug(`obsCameraInput enabled is ${obsCameraInput.enabled}`);
        logger.debug(`obsCameraInput name is ${obsCameraInput.name}`);
        logger.debug(`obsCameraInput status is ${obsCameraInput.status}`);
        logger.debug(`obsCameraInput width is ${obsCameraInput.width}`);
        if (obsCameraInput.width === 0) {
            const waitMs = 500 * i;
            logger.debug(`Waiting for ${waitMs}ms until camera get initialized.`);
            busySleep(waitMs); // We can't use async/await here
        }
    }

    if (obsCameraInput.width === 0) {
        // console.log(`doesn't seem to work as its reported width is still zero`)
        logger.debug(`Found camera "${cameraItems[0].name}" doesn't seem to work as its reported width is still zero.`);
        return null;
    }

    return obsCameraInput;
}


function publishStreamToAgora() {

}


function setupPreview(bounds, type) {


    // console.log('OBS_settings_getVideoDevices')
    // console.log(osnJssdk.NodeObs.OBS_settings_getVideoDevices())

    displayId = UtilToolService.createUUID();

    var context = osnJssdk.VideoFactory.create();

    // console.log(JSON.parse(JSON.stringify(context)))
    //
    // logger.debug('context is', context);
    //
    context.video = {
        fpsNum: 60,
        fpsDen: 1,
        baseWidth: 1280,
        baseHeight: 720,
        outputWidth: 1280,
        outputHeight: 720,
        // outputFormat: osnJssdk.EVideoFormat.NV12,
        // colorspace: osnJssdk.EColorSpace.CS709,
        // range: osnJssdk.ERangeType.Partial,
        // scaleType: osnJssdk.EScaleType.Bilinear,
        // fpsType: osnJssdk.EFPSType.Fractional
    };


    // context.canvasId = 'previewOsnCanvas'
    // context.video.canvasId = 'previewOsnCanvas'
    // const vanvasEl = document.getElementById('previewOsnCanvas')

    const {scene, sceneName} = createScene(type);


    //这几行代码很关键啊？？
    const defaultTransition = osnJssdk.TransitionFactory.create('cut_transition', UtilToolService.createUUID());
    defaultTransition.set(scene);
    osnJssdk.Global.setOutputSource(0, defaultTransition);


    // const source = createSource()

    // logger.log(`context is ${JSON.stringify(context)}`)
    const electronWindow = UtilToolService.getMainWindow();
    // osnJssdk.NodeObs.OBS_content_createSourcePreviewDisplay(
    //   electronWindow.getNativeWindowHandle(),
    //   scene.name,
    //   displayId,
    //   false,
    //   context
    // );

    logger.debug(`scene is`, scene);
    logger.debug('context is', context);
    const items = osnJssdk.SceneFactory.fromName(sceneName).getItems();

    for (const item of items) {
        console.log(item.source);
    }

    var renderingMode = 0;


    osnJssdk.NodeObs.OBS_content_createDisplay(
        electronWindow.getNativeWindowHandle(),
        displayId,
        renderingMode,
        false,
        context,
    );

    osnJssdk.NodeObs.OBS_content_setShouldDrawUI(displayId, true);
    osnJssdk.NodeObs.OBS_content_setPaddingSize(displayId, 0);
    // Match padding color with main window background color
    osnJssdk.NodeObs.OBS_content_setPaddingColor(displayId, 255, 255, 255);



    //1.回调式
    // setInterval(()=>{
    //     osnJssdk.NodeObs.OBS_content_getDisplayImage(displayId,function (data){
    //         logger.debug('OBS_content_getDisplayImage call run')
    //         console.log(data)
    //         console.log(arguments)
    //     })
    // },1000)

    //2.直接给返回值式
    setInterval(()=>{
        const ret = osnJssdk.NodeObs.OBS_content_getDisplayImage(displayId)
        logger.debug('OBS_content_getDisplayImage_Ret is',ret)
    },1000)


    return resizePreview(bounds);
}


// Get information about prinary display
function displayInfo() {
    // const { screen } = require('electron');

    // console.log(screen)
    // const primaryDisplay = screen.getPrimaryDisplay();

    const primaryDisplay = getPrimaryDisplayAsync();

    console.log({primaryDisplay});
    const {width, height} = primaryDisplay.size;
    const {scaleFactor} = primaryDisplay;
    return {
        width,
        height,
        scaleFactor: scaleFactor,
        aspectRatio: width / height,
        physicalWidth: width * scaleFactor,
        physicalHeight: height * scaleFactor,
    };
}

function resizePreview(bounds) {
    let {aspectRatio, scaleFactor} = displayInfo();

    const displayWidth = Math.floor(bounds.width);
    const displayHeight = Math.round(displayWidth / aspectRatio);
    const displayX = Math.floor(bounds.x);
    const displayY = Math.floor(bounds.y);
    if (initY === 0) {
        initY = displayY;
    }

    osnJssdk.NodeObs.OBS_content_resizeDisplay(displayId, displayWidth * scaleFactor, displayHeight * scaleFactor);
    osnJssdk.NodeObs.OBS_content_moveDisplay(displayId, displayX * scaleFactor, displayY * scaleFactor);

    return {height: displayHeight};
}

class ObsQuickTools {

    static async startObsInstance({bounds, type = 'screen'}) {
        return setupPreview(bounds, type);
    }


    // static async startAgora({bounds, type = 'screen'}) {
    //     return publishStreamToAgora(bounds, type);
    // }

    static createObsHandler(needDefaultVideoContext = false) {

        if (window.obsInstance) return window.obsInstance;
        window.obsInstance = new OBSHandler('i-obs', needDefaultVideoContext);
        return window.obsInstance;
    }

}

module.exports = {

    ObsQuickTools
}
